package com.hudilao.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hudilao.common.BaseContext;
import com.hudilao.common.R;
import com.hudilao.dto.DishSaleDTO;
import com.hudilao.entity.Dish;
import com.hudilao.entity.Order;
import com.hudilao.entity.OrderDetail;
import com.hudilao.mapper.DishMapper;
import com.hudilao.mapper.OrderDetailMapper;
import com.hudilao.service.DishService;
import com.hudilao.service.OrderDetailService;
import com.hudilao.service.OrderService;
import com.hudilao.service.RecommendationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class RecommendationServiceImpl implements RecommendationService {

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderService orderService;

    @Autowired
    private DishMapper dishMapper;

    @Autowired
    private OrderDetailService orderDetailService;

    /**
     * 基于用户的协同过滤，根据用户习惯推荐一道他可能感兴趣的菜品
     * @return
     */
    public Dish getRecommendDish(){

        //获取当前用户
        long customerId = BaseContext.getCurrentId();

        //计算用户之间的相似度
        Map<Long,Double> customerSimilarities = calculateCustomerSimilarities(customerId);

        //按照相似度从高到低排序，找出与该用户最相似的5个用户
        List<Long> similarCustomerIds = customerSimilarities.entrySet().stream()//获取所有键值对流
                .sorted(Map.Entry.<Long,Double>comparingByValue().reversed())
                .limit(5)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());

        //获取相似用户点过的菜品
        Map<Long, Double> recommendedDishScores = new HashMap<>();
        for (Long similarCustomerId : similarCustomerIds) {
            List<OrderDetail> orderDetails = orderDetailService.getOrderDetailsByCustomerId(customerId);
            for (OrderDetail orderDetail : orderDetails) {
                Long dishId = orderDetail.getItemId();
                double similarity = customerSimilarities.get(similarCustomerId);
                recommendedDishScores.merge(dishId, similarity, Double::sum);
            }
        }

        // 排除当前用户已点过的菜品
        List<OrderDetail> currentCustomerOrderDetails = orderDetailService.getOrderDetailsByCustomerId(customerId);
        Set<Long> currentUserDishIds = currentCustomerOrderDetails.stream().map(OrderDetail::getItemId).collect(Collectors.toSet());
        recommendedDishScores.keySet().removeAll(currentUserDishIds);

        // 如果没有推荐的菜品，则随机返回一个推荐菜品，或销量前五的菜品之一
        if (recommendedDishScores.isEmpty()) {
            List<Dish> allDishes = dishMapper.selectList(null);
            if (!allDishes.isEmpty()) {
                // 获取销量前五的菜品
                List<DishSaleDTO> topSellingDishes = orderDetailService.getTopSellingDishes("weekly");
                List<Dish> topDishes = topSellingDishes.stream()
                        .map(dishSaleDTO -> dishMapper.selectById(dishSaleDTO.getDishId()))
                        .collect(Collectors.toList());

                if (!topDishes.isEmpty()) {
                    return topDishes.get(new Random().nextInt(topDishes.size()));
                } else {
                    return allDishes.get(new Random().nextInt(allDishes.size()));
                }
            } else {
                return null; // 如果没有任何菜品则返回null或抛出异常
            }
        }

        // 获取评分最高的推荐菜品ID
        Long recommendedDishId = recommendedDishScores.entrySet().stream()
                .max(Map.Entry.comparingByValue())
                .map(Map.Entry::getKey)
                .orElse(null);

        if (recommendedDishId == null) {
            return null;
        }

        return dishMapper.selectById(recommendedDishId);
    }

    /**
     * 计算用户之间相似度  Map<当前用户id,相似度>
     * @param targetCustomerId
     * @return
     */
    private Map<Long, Double> calculateCustomerSimilarities(Long targetCustomerId) {
        // 计算用户之间的相似度
        Map<Long, Map<Long, Integer>> userDishMatrix = getCustomerDishMatrix();
        Map<Long, Integer> targetCustomerDishes = userDishMatrix.get(targetCustomerId);

        if(targetCustomerDishes == null){
            return Collections.emptyMap();
        }


        Map<Long, Double> userSimilarities = new HashMap<>();

        for (Map.Entry<Long, Map<Long, Integer>> entry : userDishMatrix.entrySet()) {

            //获取当前遍历到的用户，跳过当前用户
            Long customerId = entry.getKey();
            if (customerId.equals(targetCustomerId)) {
                continue;
            }

            Map<Long, Integer> otherUserDishes = entry.getValue();
            double similarity = calculateCosineSimilarity(targetCustomerDishes, otherUserDishes);
            userSimilarities.put(customerId, similarity);
        }


        // 计算相似度逻辑
        return userSimilarities;
    }

    /**
     * 生成用户-菜品矩阵   Map<用户id,Map<菜品id,菜品数量>>
     * @return
     */
    public Map<Long,Map<Long,Integer>> getCustomerDishMatrix(){

        Map<Long, Map<Long, Integer>> userDishMatrix = new HashMap<>();

        //获取所有订单详情
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(null);

        for(OrderDetail orderDetail : orderDetails){
            //查找到该订单详情对应的用户
            long orderId = orderDetail.getOrderId();
            LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Order::getId,orderId);
            Order order = orderService.getOne(queryWrapper);
            long customerId = order.getCustomerId();

            if(orderDetail.getType() == 1){
                long dishId = orderDetail.getItemId();
                Integer quantity = orderDetail.getQuantity();

                userDishMatrix.computeIfAbsent(customerId, k -> new HashMap<>())
                        .merge(dishId, quantity, Integer::sum);
            }

        }

        return userDishMatrix;
    }

    /**
     *
     * @param customer1
     * @param customer2
     * @return
     */
    public double calculateCosineSimilarity(Map<Long, Integer> customer1, Map<Long, Integer> customer2){

        //获取两个用户都评价过的菜品
        Set<Long> commonDishes = new HashSet<>(customer1.keySet());
        commonDishes.retainAll(customer2.keySet());

        if(commonDishes.isEmpty()){
            return 0.0;
        }

        double dotProduct = 0.0;
        double magnitudeUser1 = 0.0;
        double magnitudeUser2 = 0.0;

        for (Long dishId : commonDishes) {
            dotProduct += customer1.get(dishId) * customer2.get(dishId);
        }

        for (Integer quantity : customer1.values()) {
            magnitudeUser1 += Math.pow(quantity, 2);
        }

        for (Integer quantity : customer2.values()) {
            magnitudeUser2 += Math.pow(quantity, 2);
        }

        return dotProduct / (Math.sqrt(magnitudeUser1) * Math.sqrt(magnitudeUser2));

    }

}
